﻿namespace FastColoredTextBoxNS
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Text;

    /// <summary>
    /// 	Line of text
    /// </summary>
    public class Line : IList<Char>
    {
        protected List<Char> chars;

        public string FoldingStartMarker { get; set; }

        public string FoldingEndMarker { get; set; }

        /// <summary>
        /// 	Text of line was changed
        /// </summary>
        public bool IsChanged { get; set; }

        /// <summary>
        /// 	Time of last visit of caret in this line
        /// </summary>
        /// <remarks>
        /// 	This property can be used for forward/backward navigating
        /// </remarks>
        public DateTime LastVisit { get; set; }

        /// <summary>
        /// 	Background brush.
        /// </summary>
        public Brush BackgroundBrush { get; set; }

        /// <summary>
        /// 	Unique ID
        /// </summary>
        public int UniqueId { get; private set; }

        /// <summary>
        /// 	Count of needed start spaces for AutoIndent
        /// </summary>
        public int AutoIndentSpacesNeededCount { get; set; }

        internal Line(int uid)
        {
            this.UniqueId = uid;
            this.chars = new List<Char>();
        }

        /// <summary>
        /// 	Clears style of chars, delete folding markers
        /// </summary>
        public void ClearStyle(StyleIndex styleIndex)
        {
            this.FoldingStartMarker = null;
            this.FoldingEndMarker = null;
            for (int i = 0; i < this.Count; i++)
            {
                Char c = this[i];
                c.style &= ~styleIndex;
                this[i] = c;
            }
        }

        /// <summary>
        /// 	Text of the line
        /// </summary>
        public virtual string Text
        {
            get
            {
                var sb = new StringBuilder(this.Count);
                foreach (var c in this)
                {
                    sb.Append(c.c);
                }
                return sb.ToString();
            }
        }

        /// <summary>
        /// 	Clears folding markers
        /// </summary>
        public void ClearFoldingMarkers()
        {
            this.FoldingStartMarker = null;
            this.FoldingEndMarker = null;
        }

        /// <summary>
        /// 	Count of start spaces
        /// </summary>
        public int StartSpacesCount
        {
            get
            {
                int spacesCount = 0;
                for (int i = 0; i < this.Count; i++)
                {
                    if (this[i].c == ' ')
                    {
                        spacesCount++;
                    }
                    else
                    {
                        break;
                    }
                }
                return spacesCount;
            }
        }

        public int IndexOf(Char item)
        {
            return this.chars.IndexOf(item);
        }

        public void Insert(int index, Char item)
        {
            this.chars.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            this.chars.RemoveAt(index);
        }

        public Char this[int index]
        {
            get
            {
                return this.chars[index];
            }
            set
            {
                this.chars[index] = value;
            }
        }

        public void Add(Char item)
        {
            this.chars.Add(item);
        }

        public void Clear()
        {
            this.chars.Clear();
        }

        public bool Contains(Char item)
        {
            return this.chars.Contains(item);
        }

        public void CopyTo(Char[] array, int arrayIndex)
        {
            this.chars.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// 	Chars count
        /// </summary>
        public int Count
        {
            get
            {
                return this.chars.Count;
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        public bool Remove(Char item)
        {
            return this.chars.Remove(item);
        }

        public IEnumerator<Char> GetEnumerator()
        {
            return this.chars.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.chars.GetEnumerator();
        }

        public virtual void RemoveRange(int index, int count)
        {
            if (index >= this.Count)
            {
                return;
            }
            this.chars.RemoveRange(index, Math.Min(this.Count - index, count));
        }

        public virtual void TrimExcess()
        {
            this.chars.TrimExcess();
        }

        public virtual void AddRange(IEnumerable<Char> collection)
        {
            this.chars.AddRange(collection);
        }
    }

    public struct LineInfo
    {
        private List<int> cutOffPositions;

        //Y coordinate of line on screen
        internal int startY;

        internal int bottomPadding;

        //indent of secondary wordwrap strings (in chars)
        internal int wordWrapIndent;

        /// <summary>
        /// 	Visible state
        /// </summary>
        public VisibleState VisibleState;

        public LineInfo(int startY)
        {
            this.cutOffPositions = null;
            this.VisibleState = VisibleState.Visible;
            this.startY = startY;
            this.bottomPadding = 0;
            this.wordWrapIndent = 0;
        }

        /// <summary>
        /// 	Positions for wordwrap cutoffs
        /// </summary>
        public List<int> CutOffPositions
        {
            get
            {
                if (this.cutOffPositions == null)
                {
                    this.cutOffPositions = new List<int>();
                }
                return this.cutOffPositions;
            }
        }

        /// <summary>
        /// 	Count of wordwrap string count for this line
        /// </summary>
        public int WordWrapStringsCount
        {
            get
            {
                switch (this.VisibleState)
                {
                    case VisibleState.Visible:
                        if (this.cutOffPositions == null)
                        {
                            return 1;
                        }
                        else
                        {
                            return this.cutOffPositions.Count + 1;
                        }
                    case VisibleState.Hidden:
                        return 0;
                    case VisibleState.StartOfHiddenBlock:
                        return 1;
                }

                return 0;
            }
        }

        internal int GetWordWrapStringStartPosition(int iWordWrapLine)
        {
            return iWordWrapLine == 0 ? 0 : this.CutOffPositions[iWordWrapLine - 1];
        }

        internal int GetWordWrapStringFinishPosition(int iWordWrapLine, Line line)
        {
            if (this.WordWrapStringsCount <= 0)
            {
                return 0;
            }
            return iWordWrapLine == this.WordWrapStringsCount - 1
                       ? line.Count - 1
                       : this.CutOffPositions[iWordWrapLine] - 1;
        }

        /// <summary>
        /// 	Gets index of wordwrap string for given char position
        /// </summary>
        public int GetWordWrapStringIndex(int iChar)
        {
            if (this.cutOffPositions == null || this.cutOffPositions.Count == 0)
            {
                return 0;
            }
            for (int i = 0; i < this.cutOffPositions.Count; i++)
            {
                if (this.cutOffPositions[i] > /*>=*/ iChar)
                {
                    return i;
                }
            }
            return this.cutOffPositions.Count;
        }
    }

    public enum VisibleState : byte
    {
        Visible,

        StartOfHiddenBlock,

        Hidden
    }

    public enum IndentMarker
    {
        None,

        Increased,

        Decreased
    }
}